package com.aocore.awesomo;

import com.aocore.awesomo.api.EventManager;
import com.aocore.awesomo.api.events.GameLogonSuccessEvent;
import com.aocore.awesomo.api.events.GameOverEvent;
import com.aocore.awesomo.api.events.LifeManaChangeEvent;
import com.aocore.awesomo.api.events.SendMessageEvent;
import com.aocore.d2data.AreaLevel;
import com.aocore.d2data.GameDifficulty;
import com.aocore.d2data.UnitType;
import com.aocore.map.GameMap;
import com.aocore.packet.CCPacket;
import com.aocore.packet.CSPacket;
import com.aocore.packet.GC.SendMessage;
import com.aocore.packet.GCPacket;
import com.aocore.packet.GS.*;
import com.aocore.packet.GSPacket;
import com.aocore.packet.Packet;
import com.aocore.packet.RCPacket;
import com.aocore.packet.RSPacket;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;

/**
 *
 * @author derek
 */
public abstract class Bot extends Thread implements EventManager
{
    public abstract void sendPacket(CSPacket packet) throws IOException;
    public abstract void sendPacket(CCPacket packet) throws IOException;
    public abstract void sendPacket(RSPacket packet) throws IOException;
    public abstract void sendPacket(RCPacket packet) throws IOException;
    public abstract void sendPacket(GSPacket packet) throws IOException;
    public abstract void sendPacket(GCPacket packet) throws IOException;
    protected abstract void fireEvents(Class event, Object... args);

    // game info
    private GameDifficulty difficulty;
    private boolean hardcore;
    private boolean expansion;
    private boolean ladder;

    // my info
    private Player me;
    private int gold; //TODO need long?
    private int experience; //TODO need long?

    // map info
    private GameMap maps;

    private List<Warp> warps;
    private List<Player> players = new LinkedList<Player>();
    private List<Npc> npcs = new LinkedList<Npc>();

    public AreaLevel getArea()
    {
        if (maps == null)
        {
            return null;
        }

        return maps.getArea(me);
    }

    public Player getPlayer(int uid)
    {
        for (Player player : players)
        {
            if (player.getUid() == uid)
            {
                return player;
            }
        }

        return null;
    }

    public Npc getNpc(int uid)
    {
        for (Npc npc : npcs)
        {
            if (npc.getUid() == uid)
            {
                return npc;
            }
        }

        return null;
    }

    public Npc getClosestNpc()
    {
        Npc result = null;

        for (Npc npc : npcs)
        {
            if (npc.isAlive())
            {
                if (result == null)
                {
                    result = npc;
                }
                else if (Math.sqrt(Math.pow(result.getX() - me.getX(), 2) +  Math.pow(result.getY() - me.getY(), 2)) >
                         Math.sqrt(Math.pow(npc.getX() - me.getX(), 2) +  Math.pow(npc.getY() - me.getY(), 2)))
                {
                    result = npc;
                }
            }
        }

        return result;
    }

    protected Packet process(SendMessage sendMessage)
    {
        fireEvents(SendMessageEvent.class, sendMessage.getMessage());
        return sendMessage;
    }

    protected Packet process(GameLogonReceipt gameLogonReceipt)
    {
        difficulty = gameLogonReceipt.getDifficulty();
        hardcore = gameLogonReceipt.isHardcore();
        expansion = gameLogonReceipt.isExpansion();
        ladder = gameLogonReceipt.isLadder();
        return gameLogonReceipt;
    }

    protected Packet process(GameLogonSuccess gameLogonSuccess)
    {
        fireEvents(GameLogonSuccessEvent.class);
        return gameLogonSuccess;
    }

    protected Packet process(LoadAct loadAct) throws IOException
    {
        if (maps == null)
        {
            maps = new GameMap(loadAct.getMapId(), difficulty);
        }

        return loadAct;
    }

    protected Packet process(MapAdd mapAdd) throws IOException
    {
        maps.addArea(mapAdd.getArea());
        return mapAdd;
    }

    protected Packet process(MapRemove mapRemove)
    {
        return mapRemove;
    }

    protected Packet process(AssignWarp assignWarp)
    {
        Warp warp = new Warp(assignWarp.getUid());
        warp.setUnitType(assignWarp.getUnitType());
        warp.setWarpType(assignWarp.getWarpType());
        warp.setX(assignWarp.getX());
        warp.setY(assignWarp.getY());
        warps.add(warp);

        return assignWarp;
    }

    protected Packet process(RemoveGroundUnit removeGroundUnit)
    {
        return removeGroundUnit;
    }

    protected Packet process(GameHandshake gameHandshake)
    {
        if (me != null)
        {
            System.out.println("weird, received multiple GameHandshake packets");
        }

        me = getPlayer(gameHandshake.getUid());
        return gameHandshake;
    }

    protected Packet process(NPCGetHit npcGetHit)
    {
        if (npcGetHit.getUnitType() == UnitType.NPC)
        {
            // lookup npc, and set their life.. why does old ao only do it for merc?
        }
        else
        {
            System.out.println("weird, unit type not set to NPC");
        }

        return npcGetHit;
    }

    protected Packet process(PlayerStop playerStop)
    {
        if (playerStop.getUnitType() == UnitType.Player)
        {
            Player player = getPlayer(playerStop.getUid());
            player.setLife(playerStop.getLife());
            player.setX(playerStop.getX());
            player.setY(playerStop.getY());
        }
        else
        {
            System.out.println("weird, unit type not set to Player");
        }

        return playerStop;
    }

    protected Packet process(SetGameObjectMode setGameObjectMode)
    {
        return setGameObjectMode;
    }

    protected Packet process(PlayerMove playerMove)
    {
        Player player = getPlayer(playerMove.getUid());
        //TODO should we use targetX, targetY?
        player.setX(playerMove.getCurrentX());
        player.setY(playerMove.getCurrentY());

        return playerMove;
    }

    protected Packet process(PlayerMoveToTarget playerMoveToTarget)
    {
        Player player = getPlayer(playerMoveToTarget.getUid());
        //TODO should we use targetUID?
        player.setX(playerMoveToTarget.getCurrentX());
        player.setY(playerMoveToTarget.getCurrentY());

        return playerMoveToTarget;
    }

    protected Packet process(ReportKill reportKill)
    {
        return reportKill;
    }
    
    protected Packet process(PlayerReassign playerReassign)
    {
        switch (playerReassign.getUnitType())
        {
            case Player:
            {
                Player player = getPlayer(playerReassign.getUid());
                player.setX(playerReassign.getX());
                player.setY(playerReassign.getY());
                break;
            }
            case NPC:
            {
                Npc npc = getNpc(playerReassign.getUid());
                npc.setX(playerReassign.getX());
                npc.setY(playerReassign.getY());
                break;
            }
        }

        return playerReassign;
    }

    protected Packet process(PlayerLifeManaChange1 playerLifeManaChange1)
    {
        me.setLife(playerLifeManaChange1.getLife());
        me.setMana(playerLifeManaChange1.getMana());
        me.setX(playerLifeManaChange1.getX());
        me.setY(playerLifeManaChange1.getY());
        me.setStamina(playerLifeManaChange1.getStamina());
        fireEvents(LifeManaChangeEvent.class, me.getLife(), me.getMana());
        return playerLifeManaChange1;
    }

    protected Packet process(SmallGoldAdd smallGoldAdd)
    {
        gold += smallGoldAdd.getAmount();
        return smallGoldAdd;
    }

    protected Packet process(ByteToExperience byteToExperience)
    {
        experience += byteToExperience.getExperience();
        return byteToExperience;
    }

    protected Packet process(WordToExperience wordToExperience)
    {
        experience += wordToExperience.getExperience();
        return wordToExperience;
    }

    protected Packet process(DWordToExperience dWordToExperience)
    {
        experience += dWordToExperience.getExperience();
        return dWordToExperience;
    }

    protected Packet process(AttributeByte attributeByte)
    {
        return attributeByte;
    }

    protected Packet process(AttributeWord attributeWord)
    {
        return attributeWord;
    }

    protected Packet process(AttributeDWord attributeDWord)
    {
        return attributeDWord;
    }

    protected Packet process(PlayerAttributeNotification playerAttributeNotification)
    {
        return playerAttributeNotification;
    }

    protected Packet process(UpdateSkill updateSkill)
    {
        Player player = getPlayer(updateSkill.getUid());
        Skill skill = player.getSkill(updateSkill.getSkill());

        if (skill == null)
        {
            skill = new Skill(updateSkill.getSkill());
            player.addSkill(skill);
        }

        skill.setBaseLevel(updateSkill.getBaseLevel());
        skill.setBonus(updateSkill.getBonus());

        return updateSkill;
    }

    protected Packet process(UpdateItemSkill updateItemSkill)
    {
        if (me.getUid() != updateItemSkill.getUid())
        {
            System.out.println("weird, uid should always be mine?");
        }
        //TODO book of tp/id... how to store this?
        return updateItemSkill;
    }

    protected Packet process(AssignSkill assignSkill)
    {
        if (assignSkill.getUnitType() == UnitType.Player)
        {
            Player player = getPlayer(assignSkill.getUid());
            player.assignSkill(assignSkill.getHand(), assignSkill.getSkill());
        }
        else
        {
            //TODO can we do more with this packet?
            System.out.println("assign " + assignSkill.getSkill() + " to hand " + assignSkill.getSkill() + " for " + assignSkill.getUnitType());
        }

        return assignSkill;
    }

    protected Packet process(GameMessage gameMessage)
    {
        return gameMessage;
    }

    protected Packet process(NPCInfo npcInfo)
    {
        return npcInfo;
    }

    protected Packet process(UpdateQuestInfo updateQuestInfo)
    {
        return updateQuestInfo;
    }

    protected Packet process(UpdateGameQuestLog updateGameQuestLog)
    {
        return updateGameQuestLog;
    }

    protected Packet process(TransactionComplete transactionComplete)
    {
        return transactionComplete;
    }

    protected Packet process(PlaySound playSound)
    {
        return playSound;
    }

    protected Packet process(UpdateItemStats updateItemStats)
    {
        return updateItemStats;
    }

    protected Packet process(UseStackableItem useStackableItem)
    {
        return useStackableItem;
    }

    protected Packet process(PlayerClearCursor playerClearCursor)
    {
        return playerClearCursor;
    }

    protected Packet process(Relator1 relator1)
    {
        return relator1;
    }

    protected Packet process(Relator2 relator2)
    {
        return relator2;
    }

    protected Packet process(UnitUseSkillOnTarget unitUseSkillOnTarget)
    {
        return unitUseSkillOnTarget;
    }

    protected Packet process(UnitUseSkill unitUseSkill)
    {
        return unitUseSkill;
    }

    protected Packet process(MercForHire mercForHire)
    {
        return mercForHire;
    }

    protected Packet process(MercForHireListStart mercForHireListStart)
    {
        return mercForHireListStart;
    }

    protected Packet process(AssignGameObject assignGameObject)
    {
        return assignGameObject;
    }

    protected Packet process(UpdateQuestLog updateQuestLog)
    {
        return updateQuestLog;
    }

    protected Packet process(PartyRefresh partyRefresh)
    {
        return partyRefresh;
    }

    protected Packet process(AssignPlayer assignPlayer)
    {
        Player player = new Player(assignPlayer.getUid());
        player.setCharClass(assignPlayer.getCharClass());
        player.setName(assignPlayer.getName());
        player.setX(assignPlayer.getX());
        player.setY(assignPlayer.getY());
        players.add(player);

        return assignPlayer;
    }

    protected Packet process(InformationMessage informationMessage)
    {
        return informationMessage;
    }

    protected Packet process(PlayerInGame playerInGame)
    {
        //Player player = new Player(playerInGame.getUid());
        Player player = getPlayer(playerInGame.getUid());
        player.setName(playerInGame.getName());
        player.setCharClass(playerInGame.getCharClass());
        player.setLevel(playerInGame.getLevel());
        player.setPartyID(playerInGame.getPartyID());
        players.add(player);

        return playerInGame;
    }

    protected Packet process(PlayerLeaveGame playerLeaveGame)
    {
        Player player = getPlayer(playerLeaveGame.getUid());
        players.remove(player);
        return playerLeaveGame;
    }

    protected Packet process(QuestItemState questItemState)
    {
        return questItemState;
    }

    protected Packet process(PortalInfo portalInfo)
    {
        return portalInfo;
    }

    protected Packet process(OpenWaypoint openWaypoint)
    {
        return openWaypoint;
    }

    protected Packet process(PlayerKillCount playerKillCount)
    {
        Player player = getPlayer(playerKillCount.getUid());
        player.setKillCount(playerKillCount.getCount());
        return playerKillCount;
    }

    protected Packet process(NPCMove npcMove)
    {
        Npc npc = getNpc(npcMove.getUid());
        npc.setX(npcMove.getX());
        npc.setY(npcMove.getY());
        return npcMove;
    }

    protected Packet process(NPCMoveToTarget npcMoveToTarget)
    {
        Npc npc = getNpc(npcMoveToTarget.getUid());
        npc.setX(npcMoveToTarget.getCurrentX());
        npc.setY(npcMoveToTarget.getCurrentY());
        //TODO use target like old AO?
        return npcMoveToTarget;
    }

    protected Packet process(SetNPCMode setNPCMode)
    {
        Npc npc = getNpc(setNPCMode.getUid());
        npc.setMode(setNPCMode.getMode());
        npc.setX(setNPCMode.getX());
        npc.setY(setNPCMode.getY());
        npc.setLife(setNPCMode.getLife());
        return setNPCMode;
    }

    protected Packet process(NPCAction npcAction)
    {
        Npc npc = getNpc(npcAction.getUid());
        npc.setX(npcAction.getX());
        npc.setY(npcAction.getY());
        return npcAction;
    }

    protected Packet process(MonsterAttack monsterAttack)
    {
        Npc npc = getNpc(monsterAttack.getUid());
        npc.setX(monsterAttack.getX());
        npc.setY(monsterAttack.getY());
        return monsterAttack;
    }

    protected Packet process(NPCStop npcStop)
    {
        Npc npc = getNpc(npcStop.getUid());
        npc.setX(npcStop.getX());
        npc.setY(npcStop.getY());
        npc.setLife(npcStop.getLife());
        return npcStop;
    }

    protected Packet process(PlayerCorpseVisible playerCorpseVisible)
    {
        return playerCorpseVisible;
    }

    protected Packet process(AboutPlayer aboutPlayer)
    {
        return aboutPlayer;
    }

    protected Packet process(PlayerInSight playerInSight)
    {
        return playerInSight;
    }

    protected Packet process(UpdateItemUI updateItemUI)
    {
        return updateItemUI;
    }

    protected Packet process(AcceptTrade acceptTrade)
    {
        return acceptTrade;
    }

    protected Packet process(GoldTrade goldTrade)
    {
        return goldTrade;
    }

    protected Packet process(SummonAction summonAction)
    {
        return summonAction;
    }

    protected Packet process(AssignSkillHotkey assignSkillHotkey)
    {
        return assignSkillHotkey;
    }

    protected Packet process(UseSpecialItem useSpecialItem)
    {
        return useSpecialItem;
    }

    protected Packet process(SetItemState setItemState)
    {
        return setItemState;
    }

    protected Packet process(PartyMemberUpdate partyMemberUpdate)
    {
        return partyMemberUpdate;
    }

    protected Packet process(AssignMerc assignMerc)
    {
        return assignMerc;
    }

    protected Packet process(PortalOwnership portalOwnership)
    {
        return portalOwnership;
    }

    protected Packet process(NPCWantsInteract npcWantsInteract)
    {
        return npcWantsInteract;
    }

    protected Packet process(PlayerPartyRelationship playerPartyRelationship)
    {
        return playerPartyRelationship;
    }

    protected Packet process(PlayerRelationship playerRelationship)
    {
        return playerRelationship;
    }

    protected Packet process(AssignPlayerToParty assignPlayerToParty)
    {
        return assignPlayerToParty;
    }

    protected Packet process(AssignPlayerCorpse assignPlayerCorpse)
    {
        return assignPlayerCorpse;
    }

    protected Packet process(Pong pong)
    {
        return pong;
    }

    protected Packet process(PartyMemberPulse partyMemberPulse)
    {
        return partyMemberPulse;
    }

    protected Packet process(SkillsLog skillsLog)
    {
        return skillsLog;
    }

    protected Packet process(PlayerLifeManaChange playerLifeManaChange)
    {
        me.setLife(playerLifeManaChange.getLife());
        me.setMana(playerLifeManaChange.getMana());
        me.setX(playerLifeManaChange.getX());
        me.setY(playerLifeManaChange.getY());
        me.setStamina(playerLifeManaChange.getStamina());
        fireEvents(LifeManaChangeEvent.class, me.getLife(), me.getMana());
        return playerLifeManaChange;
    }

    protected Packet process(WalkVerify walkVerify)
    {
        me.setX(walkVerify.getX());
        me.setY(walkVerify.getY());
        me.setStamina(walkVerify.getStamina());
        return walkVerify;
    }

    protected Packet process(SwitchWeaponSet switchWeaponSet)
    {
        return switchWeaponSet;
    }

    protected Packet process(ItemTriggerSkill itemTriggerSkill)
    {
        return itemTriggerSkill;
    }

    protected Packet process(WorldItemAction worldItemAction)
    {
        return worldItemAction;
    }

    protected Packet process(OwnedItemAction ownedItemAction)
    {
        return ownedItemAction;
    }

    protected Packet process(MercAttributeByte mercAttributeByte)
    {
        return mercAttributeByte;
    }

    protected Packet process(MercAttributeWord mercAttributeWord)
    {
        return mercAttributeWord;
    }

    protected Packet process(MercAttributeDWord mercAttributeDWord)
    {
        return mercAttributeDWord;
    }

    protected Packet process(MercByteToExperience mercByteToExperience)
    {
        return mercByteToExperience;
    }

    protected Packet process(MercWordToExperience mercWordToExperience)
    {
        return mercWordToExperience;
    }

    protected Packet process(DelayedState delayedState)
    {
        return delayedState;
    }

    protected Packet process(SetState setState)
    {
        return setState;
    }

    protected Packet process(EndState endState)
    {
        return endState;
    }

    protected Packet process(AddUnit addUnit)
    {
        return addUnit;
    }

    protected Packet process(NPCHeal npcHeal)
    {
        Npc npc = getNpc(npcHeal.getUid());
        npc.setLife(npcHeal.getLife());
        return npcHeal;
    }

    protected Packet process(AssignNPC assignNPC)
    {
        Npc npc = new Npc(assignNPC.getUid());
        npc.setId(assignNPC.getId());
        npc.setX(assignNPC.getX());
        npc.setY(assignNPC.getY());
        npc.setLife(assignNPC.getLife());
        npcs.add(npc);

        return assignNPC;
    }

    protected Packet process(WardenCheck wardenCheck)
    {
        return wardenCheck;
    }

    protected Packet process(RequestLogonInfo requestLogonInfo)
    {
        return requestLogonInfo;
    }

    protected Packet process(GameOver gameOver)
    {
        fireEvents(GameOverEvent.class);
        return gameOver;
    }
}
